# -*-coding:utf-8-*-

from bson.objectid import ObjectId
from daos.base import BaseDao


class AreasDao(BaseDao):
    def __init__(self, db):
        super(AreasDao, self).__init__(db, 'areas')

    def get_area_list(self, uid):
        result = []
        for a in self.find({'admin': uid}, {'_id': 1, 'name': 1}):
            a['aid'] = str(a['_id'])
            del a['_id']
            result.append(a)
        return result

    def create_area(self, uid, data):
        area = {
            'admin': uid,
            'name': data['name'],
        }
        self.insert_one(area)
        return {'aid': str(area['_id'])}

    def update_area(self, uid, aid, data):
        filter_cond = {'admin': uid, '_id': ObjectId(aid)}
        data = self.extract_fields(data, optional_fields=['name'])
        if 0 == len(data):
            return False
        result = self.update_one(filter_cond, {'$set': data})
        return 1 == result.matched_count

    def delete_area(self, uid, aid):
        filter_cond = {'admin': uid, '_id': ObjectId(aid)}
        result = self.delete_one(filter_cond)
        return 1 == result.deleted_count

    def get_partition_list(self, uid, aid):
        result = []
        area = self.find_one({'admin': uid, '_id': ObjectId(aid)},
                             {'partitions.pid': 1,
                              'partitions.type': 1,
                              'partitions.name': 1,
                              'partitions.number': 1})
        if area is not None and 'partitions' in area:
            result = area['partitions']
        return result

    def create_partition(self, uid, aid, data):
        filter_cond = {'admin': uid, '_id': ObjectId(aid)}
        partition = {
            'pid': str(ObjectId()),
            'type': data['type'],
            'name': data['name'],
        }
        # TODO: 保证 number 在 partitions 数组中的唯一性
        if partition['type'] == 'building':
            partition['number'] = data['number']
        result = self.update_one(filter_cond, {'$push': {'partitions': partition}})
        if 1 == result.modified_count:
            return {'pid': partition['pid']}

    def update_partition(self, uid, aid, pid, data):
        filter_cond = {'partitions.pid': pid}
        area = self.find_one(filter_cond, {'partitions.$': 1})
        if area is None:
            return False
        modification = {}
        if 'name' in data:
            modification['name'] = data['name']
        partition = area['partitions'][0]
        if partition['type'] == 'building' and 'number' in data:
            # TODO: 保证 number 在 partitions 数组中的唯一性
            modification['number'] = data['number']
        if len(modification) == 0:
            return False
        modification = dict(partition, **modification)
        result = self.update_one(filter_cond, {'$set': {'partitions.$': modification}})
        return 1 == result.modified_count

    def delete_partition(self, uid, aid, pid):
        filter_cond = {'admin': uid, '_id': ObjectId(aid)}
        result = self.update_one(filter_cond, {'$pull': {'partitions': {'pid': pid}}})
        # print result.raw_result
        # print result.modified_count
        return 1 == result.modified_count

    def get_entrance_list(self, uid, aid, pid):
        result = []
        area = self.find_one({'partitions.pid': pid},
                             {'partitions.$': 1})
        if area is not None and 'entrances' in area['partitions'][0]:
            for e in area['partitions'][0]['entrances']:
                out = {
                    'eid': e['eid'],
                    'name': e['name'],
                }
                if 'did' in e:
                    out['did'] = e['did']
                result.append(out)
        return result

    def create_entrance(self, uid, aid, pid, data):
        filter_cond = {'partitions.pid': pid}
        area = self.find_one(filter_cond, {'partitions.$': 1})
        if area is None:
            return False
        modification = self.extract_fields(data, optional_fields=['name', 'did'])
        if len(modification) == 0:
            return False
        modification['eid'] = str(ObjectId())
        result = self.update_one(filter_cond, {'$push': {'partitions.$.entrances': modification}})
        if 1 == result.modified_count:
            return {'eid': modification['eid']}

    def update_entrance(self, uid, aid, pid, eid, data):
        filter_cond = {'partitions.pid': pid}
        area = self.find_one(filter_cond, {'partitions.$': 1})
        if area is None:
            return False
        entrances = area['partitions'][0]['entrances']
        eidx = 0
        for e in entrances:
            if e['eid'] == eid:
                break
            eidx += 1
        if len(entrances) == eidx:
            return False
        modification = self.extract_fields(data, optional_fields=['name', 'did'])
        if len(modification) == 0:
            return False
        modification = dict(entrances[eidx], **modification)
        result = self.update_one(filter_cond, {'$set': {'partitions.$.entrances.%d' % eidx: modification}})
        return 1 == result.modified_count

    def delete_entrance(self, uid, aid, pid, eid):
        filter_cond = {'partitions.pid': pid}
        result = self.update_one(filter_cond, {'$pull': {'partitions.$.entrances': {'eid': eid}}})
        # print result.raw_result
        # print result.modified_count
        return 1 == result.modified_count

    def get_room_list(self, uid, aid, pid):
        result = []
        area = self.find_one({'partitions.pid': pid}, {'partitions.$': 1})
        if area is not None and 'rooms' in area['partitions'][0]:
            print area['partitions'][0]['rooms']
            for r in area['partitions'][0]['rooms']:
                out = {
                    'rid': r['rid'],
                    'number': r['number'],
                }
                if 'phone' in r:
                    out['phone'] = r['phone']
                if 'owner' in r:
                    out['owner'] = r['owner']
                if 'id_card_no' in r:
                    out['id_card_no'] = r['id_card_no']
                result.append(out)
        return result

    def get_room_list_by_phone_of_owner(self, phone):
        result = []
        for a in self.find({'partitions.rooms.phone': phone}):
            ps = a['partitions']
            pi_to_del = []
            sel_flag = False
            # 打描 area 中所有 building 分区
            for pi, p in enumerate(ps):
                if p['type'] != 'building' or 'rooms' not in p:
                    continue
                rs = p['rooms']
                rooms = []
                for r in rs:
                    if 'phone' in r and r['phone'] == phone:
                        if r.has_key('password'):
                            del r['password']
                        rooms.append(r)
                if 0 < len(rooms):
                    sel_flag = True
                    # 更新 partition.rooms 字段，目的是将不相关的 rooms 信息去掉
                    p['rooms'] = rooms
                else:
                    pi_to_del.append(pi)
            if sel_flag:
                if 0 < len(pi_to_del):
                    ps = []
                    for pi, p in enumerate(a['partitions']):
                        if pi not in pi_to_del:
                            ps.append(p)
                    a['partitions'] = ps
                a['aid'] = str(a['_id'])
                del a['_id']
                if 'users' in a:
                    del a['users']
                del a['admin']
                result.append(a)
        return result

    def create_room(self, uid, aid, pid, data):
        # filter_cond = {'admin': uid, '_id': ObjectId(aid), 'partitions.pid': pid, 'partitions.type': 'building'}
        # filter_cond = {'partitions.pid': pid, 'partitions.type': 'building'}
        filter_cond = {'partitions.pid': pid}  # NOTE: 只有指定一个条件才能准确定位到数组元素
        area = self.find_one(filter_cond, {'partitions.$': 1})
        if area is None:
            return False
        modification = self.extract_fields(data, required_fields=['number'],
                                           optional_fields=['phone', 'password', 'owner', 'id_card_no'])
        if len(modification) == 0:
            return False
        modification['rid'] = str(ObjectId())
        # modification = dict(partition, **modification)
        result = self.update_one(filter_cond, {'$push': {'partitions.$.rooms': modification}})
        if 1 == result.modified_count:
            return {'rid': modification['rid']}

    def update_room(self, uid, aid, pid, rid, data):
        filter_cond = {'partitions.rooms.rid': rid}
        area = self.find_one(filter_cond, {'partitions.$': 1})
        if area is None:
            return False
        rooms = area['partitions'][0]['rooms']
        ridx = 0
        for r in rooms:
            if r['rid'] == rid:
                break
            ridx += 1
        if len(rooms) == ridx:
            return False
        modification = self.extract_fields(data, optional_fields=['number', 'phone', 'password', 'owner', 'id_card_no'])
        if len(modification) == 0:
            return False
        modification = dict(rooms[ridx], **modification)
        result = self.update_one(filter_cond, {'$set': {'partitions.$.rooms.%d' % ridx: modification}})
        return 1 == result.modified_count

    def delete_room(self, uid, aid, pid, rid):
        filter_cond = {'partitions.pid': pid}
        result = self.update_one(filter_cond, {'$pull': {'partitions.$.rooms': {'rid': rid}}})
        return 1 == result.modified_count


areas = [
    {
        'admin': '596585d5559c5856ea82fd7a',
        'users': [],
        'name': '华发四季',
        'partitions': [  # NOTE: 数量需要限制
            {
                'pid': str(ObjectId()),
                'type': 'building',
                'name': '6栋一单元',
                'number': 6,
                'entrances': [  # 每个 entrance 有各种设备？目前就一种, ACM
                    {
                        'eid': str(ObjectId()),
                        'name': '大堂入口',
                        'did': str(ObjectId()),
                    },
                    {
                        'eid': str(ObjectId()),
                        'name': '车库入口',
                        'did': str(ObjectId()),
                    },
                ],
                'rooms': [  # NOTE: 数量需要限制
                    {
                        'rid': str(ObjectId()),
                        'number': 2801,  # 需要确保在 rooms 数组中唯一
                        'phone': '13400000000',
                        'password': 'PASSWORD',  # 设置了 password 才具备远程开门等权限
                    },
                    {
                        'rid': str(ObjectId()),
                        'number': 2802,  # 需要确保在 rooms 数组中唯一
                        'phone': '13400000000',
                    },
                ]
            },
            {
                'pid': str(ObjectId()),
                'type': 'building',
                'name': '8栋一单元',
                'number': 8,
                'entrances': [  # 每个 entrance 有各种设备？目前就一种, ACM
                    {
                        'eid': str(ObjectId()),
                        'name': '大堂入口',
                        'did': str(ObjectId()),
                    },
                    {
                        'eid': str(ObjectId()),
                        'name': '车库入口',
                        'did': str(ObjectId()),
                    },
                ],
                'rooms': [  # NOTE: 数量需要限制
                    {
                        'rid': str(ObjectId()),
                        'number': 801,  # 需要确保在 rooms 数组中唯一
                        'phone': '13400000000',
                    },
                    {
                        'rid': str(ObjectId()),
                        'number': 202,  # 需要确保在 rooms 数组中唯一
                        'phone': '13400000001',
                    },
                ]
            },
            {
                'pid': str(ObjectId()),
                'type': 'entrance',  # 公有的入口
                'name': '南门',
                'entrances': [
                    {
                        'eid': str(ObjectId()),
                        'name': '南门入口',
                        'did': str(ObjectId()),  # pub DEVICE_ID_2:6:2801
                    }
                ],
            }
        ]
    },
    {
        'admin': '596585d5559c5856ea82fd7a',
        'users': [],
        'name': '华发四季2',
        'partitions': [  # NOTE: 数量需要限制
            {
                'pid': str(ObjectId()),
                'type': 'building',
                'name': '1栋一单元',
                'number': 1,
                'entrances': [  # 每个 entrance 有各种设备？目前就一种, ACM
                    {
                        'eid': str(ObjectId()),
                        'name': '大堂入口',
                        'did': str(ObjectId()),
                    },
                    {
                        'eid': str(ObjectId()),
                        'name': '车库入口',
                        'did': str(ObjectId()),
                    },
                ],
                'rooms': [  # NOTE: 数量需要限制
                    {
                        'rid': str(ObjectId()),
                        'number': 3801,  # 需要确保在 rooms 数组中唯一
                        'phone': '13400000000',
                    },
                    {
                        'rid': str(ObjectId()),
                        'number': 1802,  # 需要确保在 rooms 数组中唯一
                        'phone': '13400000001',
                    },
                ]
            },
            {
                'pid': str(ObjectId()),
                'type': 'entrance',  # 公有的入口
                'name': '北门',
                'entrances': [
                    {
                        'eid': str(ObjectId()),
                        'name': '北门入口',
                        'did': str(ObjectId()),  # pub DEVICE_ID_2:6:2801
                    }
                ],
            }
        ]
    },
]


# print result

# result = []
# import time
# s = time.time()
# for a in c.aggregate([{'$unwind': '$partitions'},
#                       {'$unwind': '$partitions.rooms'},
#                       {'$match': {'partitions.rooms.phone': '13400000000'}},
#                       {'$group': {'_id': '$partitions.pid', 'rooms': {'$push': '$partitions.rooms'},
#                                   'root': {'$first': '$$ROOT'}}},
#                       ]):
#     print a
#
# print time.time() - s

def test():
    import json
    import time
    import pymongo

    db = pymongo.MongoClient().test_db
    c = db.get_collection('areas')
    c.drop()
    c.insert_many(areas)

    s = time.time()
    phone = '13400000000'
    uid = '596585d5559c5856ea82fd7a'
    aid = str(areas[0]['_id'])
    result = AreasDao(db).get_area_list(uid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')
    print time.time() - s

    result = AreasDao(db).get_partition_list(uid, aid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    pid = result[0]['pid']

    result = AreasDao(db).get_entrance_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_room_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_room_list_by_phone_of_owner(phone)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'type': 'building',
        'name': 'building 10',
        'number': 10,
    }
    result = AreasDao(db).create_partition(uid, aid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    pid = result['pid']

    result = AreasDao(db).get_partition_list(uid, aid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'name': 'building 11',
        'number': 11,
    }
    result = AreasDao(db).update_partition(uid, aid, pid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_partition_list(uid, aid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).delete_partition(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_partition_list(uid, aid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'type': 'building',
        'name': 'building 10',
        'number': 10,
    }
    result = AreasDao(db).create_partition(uid, aid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    pid = result['pid']

    data = {
        'name': u'10栋大堂入口',
    }
    result = AreasDao(db).create_entrance(uid, aid, pid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'name': u'10栋地下车库入口',
    }
    result = AreasDao(db).create_entrance(uid, aid, pid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    eid = result['eid']

    result = AreasDao(db).get_entrance_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'name': u'10栋地下车库入口(B)',
    }
    result = AreasDao(db).update_entrance(uid, aid, pid, eid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_entrance_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).delete_entrance(uid, aid, pid, eid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_entrance_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'number': 2603,
    }
    result = AreasDao(db).create_room(uid, aid, pid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    data = {
        'number': 2604,
    }
    result = AreasDao(db).create_room(uid, aid, pid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    rid = result['rid']

    print 'update room(%s)' % rid
    data = {
        'phone': '13400000000',
        'number': 1604,
    }
    result = AreasDao(db).update_room(uid, aid, pid, rid, data)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_room_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).delete_room(uid, aid, pid, rid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_room_list(uid, aid, pid)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    result = AreasDao(db).get_room_list_by_phone_of_owner(phone)
    print json.dumps(result, indent=4, ensure_ascii=False).encode('utf-8')

    # for a in c.find({'admin': '596585d5559c5856ea82fd7a', '_id': areas[0]['_id']}):
    #     print a


if '__main__' == __name__:
    test()
